<?php
/**
 * 顶级公共控制器
 * @author bcxgame
 * @version 1.0
 */
namespace app\common\controller;
use think\exception\HttpResponseException;
use think\Controller;
use Config;
use think\Response;
use Session;
use app\common\exception\System as SystemException;

class Common extends Controller
{
	/**
     * 默认响应输出类型,支持json/xml
     * @var string
     */
    protected static $responseType = NULL;

    /**
     * 同一个接口最小间隔访问时间[秒]
     * @var integer
     */
    protected $interval = 1;

    /**
     * 同一个接口1秒钟之内请求次数
     * @var integer
     */
    protected $requestQty = 4;

    /**
     * 需要限制访问频率的模块
     */
    protected $limitModule = ['index'];

    /**
     * 公共方法初始化
     */
	protected function initialize()
	{
		// 防止多次调用
		static $commonInit = false;
		if ($commonInit) {
			return;
		}

        // 返回值类型
        if (is_null(self::$responseType)) {
            self::$responseType = Config::get('default_return_type');
        }

        // 禁用options请求
        $this->_denyRequest();

        // 应用程序状态
        $this->_systemValid();

        // 访问来源控制[线上环境]
        // $this->_refererLimit();

        // 限制访问频率[index,admin]
        $this->_accessLimit();

        // 防止session劫持
        // $this->_filterSession();

		// 线上https跳转
		$sslFlag = $this->request->isSsl();
		$isAjax  = $this->request->isAjax();

		if (Config::get('is_https') && !$sslFlag && !$isAjax) {
			header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301);
            exit();
		}

        // 设置版权
        header('X-Powered-By: ' . SITE_TOP_DOMAIN);
        header_remove('Server');
        $commonInit = true;
	}

    /**
     * 禁用请求
     */
    private function _denyRequest(): void
    {
        if ($this->request->method() == 'OPTIONS') {
            $response = $this->errorReturn(SystemException::E_METHOD, SystemException::$messageList[SystemException::E_METHOD]);
            throw new HttpResponseException($response);
        }
    }

    /**
     * 访问来源限制
     */
    private function _refererLimit(): void
    {
        if (SITE_ENVIROMENT == 'productive') {
            // 访问来源控制
            $referer = $this->request->server('HTTP_REFERER');
            if (!empty($referer) && strpos($referer, '.' . SITE_TOP_DOMAIN) === false) {
                $response = $this->errorReturn(SystemException::E_REFERER, SystemException::$messageList[SystemException::E_REFERER]);
                throw new HttpResponseException($response);
            }
        }
    }

    /**
     * 限制前台访问频率
     */
    private function _accessLimit(): void
    {
        $modulename = strtolower($this->request->module());
        if (in_array($modulename, $this->limitModule)) {
            $controllername = strtolower($this->request->controller());
            $actionname = strtolower($this->request->action());
            $sessKey = 'user_access';
            $access = Session::get($sessKey);

            $nowTime = $this->request->server('REQUEST_TIME');
            if ($access) {
                if(($nowTime - $access['time'] < $this->interval) && ($access['uri'] == $controllername . '/' . $actionname) && $access['qty'] > $this->requestQty) {
                    Session::set($sessKey, ['time' => $nowTime, 'uri' => $controllername . '/' . $actionname, 'qty' => $access['qty']+1]);

                    $response = $this->errorReturn(SystemException::E_FREQUENT, SystemException::$messageList[SystemException::E_FREQUENT]);
                    throw new HttpResponseException($response);
                } else {
                    Session::delete($sessKey);
                }
            } else {
                Session::set($sessKey, ['time' => $nowTime, 'uri' => $controllername . '/' . $actionname, 'qty' => 1]);
            }
        }
    }

    /**
     * 防止劫持Session
     */
    private function _filterSession(): void
    {
        $browserTagKey   = md5('browserTag');
        $browserTagValue = md5($this->request->server('Uuid'));

        $browserTag = Session::get($browserTagKey);
        if (empty($browserTag)) {
            Session::set($browserTagKey, $browserTagValue);
            $browserTag = $browserTagValue;
        }

        // session攻击
        if ($browserTagValue != $browserTag) {
            Session::clear();
            Session::destroy();

            $response = $this->errorReturn(SystemException::E_SESSION, SystemException::$messageList[SystemException::E_SESSION]);
            throw new HttpResponseException($response);
        }
    }

    /**
     * 系统状态
     */
    private function _systemValid(): void
    {
        if (!Config::get('app.system_status')) {
            $response = $this->errorReturn(SystemException::E_CLOSE, SystemException::$messageList[SystemException::E_CLOSE]);
            throw new HttpResponseException($response);
        }
    }

    /**
     * 操作成功返回的数据
     * @param string $msg   提示信息
     * @param mixed $data   要返回的数据
     * @param int   $code   错误码，默认为1
     * @param string $type  输出类型
     * @param array $header 发送的 Header 信息
     */
    protected function successReturn($data = null, $code = SystemException::E_SUCCESS, $msg = '', $type = null, array $header = []): Response
    {
        return $this->processResult($msg, ['list' => $data], $code, $type, $header);
    }

    /**
     * 操作失败返回的数据
     * @param string $msg   提示信息
     * @param mixed $data   要返回的数据
     * @param int   $code   错误码，默认为0
     * @param string $type  输出类型
     * @param array $header 发送的 Header 信息
     */
    protected function errorReturn($code = SystemException::E_DEFAULT, $msg = '', $data = null, $type = null, array $header = []): Response
    {
        if ($msg == '') {
            $msg = SystemException::getErrorMsg(SystemException::E_DEFAULT);
        }

        if (!isset($data['__token__']) && $this->request->isPost()) {
            $data['__token__'] = $this->request->token();
        }

        return $this->processResult($msg, $data, $code, $type, $header);
    }

    /**
     * 返回封装后的 API 数据到客户端
     * @access protected
     * @param mixed  $msg    提示信息
     * @param mixed  $data   要返回的数据
     * @param int    $code   错误码，默认为0
     * @param string $type   输出类型，支持json/xml/jsonp
     * @param array  $header 发送的 Header 信息
     * @return void
     * @throws HttpResponseException
     */
    protected function processResult($msg, $data = null, $code = SystemException::E_SUCCESS, $type = null, array $header = []): Response
    {
        $result = [
            'status'     => ($code == SystemException::E_SUCCESS) ? 'success' : 'error',
            'code'       => $code,
            'msg'        => $msg,
            'time'       => $this->request->server('REQUEST_TIME'),
            'data'       => $data,
            'bcxgame_id' => session_id(),
        ];

        // 如果未设置类型则自动判断
        $type = $type ? $type : ($this->request->param(Config::get('var_jsonp_handler')) ? 'jsonp' : self::$responseType);

        if (isset($header['statuscode']))
        {
            $code = $header['statuscode'];
            unset($header['statuscode']);
        }
        else
        {
            //未设置状态码,根据code值判断
            $code = ($code >= 1000 || $code < 200) ? 200 : $code;
        }

        return Response::create($result, $type, $code)->header($header);
    }

    /**
     * 404页面
     */
    public function _empty($name = '')
    {
        if ($this->request->isAjax()) {
            return $this->errorReturn(404, '请求不存在');
        } else {
            // 404页面
            $port = $this->request->isSsl() ? 'https://' : 'http://';
            $this->redirect($port . $this->request->host() . '/404.html', 302);
        }
    }
}